install.packages("dplyr")
install.packages("ggplot2")
install.packages("caret")
install.packages("randomForest")
set.seed(123)
library(dplyr)
library(ggplot2)
library(caret)
library(rpart)
library(utils)
library(randomForest)
randomForest 4.7-1.1
Type rfNews() to see new features/changes/bug fixes.

Attaching package: ‘randomForest’

The following object is masked from ‘package:gridExtra’:

    combine

The following object is masked from ‘package:ggplot2’:

    margin

The following object is masked from ‘package:dplyr’:

    combine
arbolado.mza.dataset <- read.csv("~/workspace/arbolado-mza/arbolado-mza-dataset.csv/arbolado-mza-dataset.csv")
arbolado.mza.dataset <- as.data.frame(arbolado.mza.dataset)

1.a

indices <- createDataPartition(arbolado.mza.dataset$inclinacion_peligrosa, p = 0.8, list = FALSE)

training_data <- arbolado.mza.dataset[indices, ]
testing_data <- arbolado.mza.dataset[-indices, ]
write.csv(testing_data, "arbolado-mendoza-validation.csv")
write.csv(training_data, "arbolado-mendoza.csv")
testing_data

2.a

Parecen haber muchos (22695 vs 2835) mas ejemplares con inclinacion no peligrosa

result <- training_data %>%
  dplyr::group_by(inclinacion_peligrosa) %>%
  dplyr::summarize(Count=n())
x_val <- barplot(result$Count, names.arg=c("No Peligrosa", "Peligrosa"))
# Add text labels on top of the bars
text(x = x_val, y = result$Count, labels = result$Count, pos = 1, cex = 1)

2.b

La seccion mas peligrosa parece ser la seccion 3.

Cabe destacar que no tenemos informacion para las secciones 9 y 10 y muy poca info de las secciones 7 y 8

result <- training_data %>%
  dplyr::group_by(seccion) %>%
  dplyr::summarize(peligrosa=sum(inclinacion_peligrosa), total=n())
ggplot(data=result, aes(x=seccion, fill="a")) +
  geom_bar(aes(y=total), stat="identity", position = "dodge", alpha=1, width=0.7, fill="darkgreen") +
  labs(title="Por seccion", x="Seccion", y="Total") +
  geom_bar(aes(y=peligrosa), data = result, stat = "identity", position="dodge", alpha = 1, width=0.7, fill="red") +
  scale_x_continuous(breaks = result$seccion, expand = c(0, 0))+
  scale_fill_manual(values = c("Total" = "darkgreen", "Peligrosas" = "red")) +
  guides(fill = guide_legend(title = "Por seccion")) +
  theme_minimal()


result <- training_data %>%
  dplyr::group_by(seccion) %>%
  dplyr::summarize(peligrosa=sum(inclinacion_peligrosa)/ifelse(n() == 0, sum(inclinacion_peligrosa), n()), total=1)

ggplot(data=result, aes(x=seccion, fill="a")) +
  geom_bar(aes(y=total), stat="identity", position = "dodge", alpha=1, width=0.7, fill="darkgreen") +
  labs(title="Por seccion", x="Seccion", y="Peligrosidad") +
  geom_bar(aes(y=peligrosa), data = result, stat = "identity", position="dodge", alpha = 1, width=0.7, fill="red") +
  scale_x_continuous(breaks = result$seccion, expand = c(0, 0))+
  scale_fill_manual(values = c("Total" = "darkgreen", "Peligrosas" = "red")) +
  guides(fill = guide_legend(title = "Por seccion Normalizada")) +
  theme_minimal()

3.c

Al parecer tenemos muchisima informacion de Moreras, en comparacion de otras especies.

Aun asi, los algarrobos parecen presentar una inclinacion peligrosa con mayor frequencia.

Dicho esto, solo tenemos 5 ejemplares de algarrobo en nuestro dataset de entrenamiento.

result <- training_data %>%
  dplyr::group_by(especie) %>%
  dplyr::summarize(peligrosa=sum(inclinacion_peligrosa), total=n())

result$especie = reorder(result$especie, -result$total)
ggplot(data=result, aes(x=especie, fill="a")) +
  geom_bar(aes(y=total), stat="identity", position = "dodge", alpha=1, width=0.7, fill="darkgreen") +
  labs(title="Por especie", x="Especie", y="Total") +
  geom_bar(aes(y=peligrosa), data = result, stat = "identity", position="dodge", alpha = 1, width=0.7, fill="red") +
  scale_x_discrete(breaks = result$especie, expand = c(0, 0))+
  scale_fill_manual(values = c("Total" = "darkgreen", "Peligrosas" = "red")) +
  guides(fill = guide_legend(title = "Por especie")) +
  theme_minimal()+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))



result <- training_data %>%
  dplyr::group_by(especie) %>%
  dplyr::summarize(peligrosa=sum(inclinacion_peligrosa)/ifelse(n() == 0, sum(inclinacion_peligrosa), n()), total=1)

result$especie = reorder(result$especie, -result$peligrosa)
ggplot(data=result, aes(x=especie, fill="a")) +
  geom_bar(aes(y=total), stat="identity", position = "dodge", alpha=1, width=0.7, fill="darkgreen") +
  labs(title="Por especie", x="Especie", y="Total") +
  geom_bar(aes(y=peligrosa), data = result, stat = "identity", position="dodge", alpha = 1, width=0.7, fill="red") +
  scale_x_discrete(breaks = result$especie, expand = c(0, 0))+
  scale_fill_manual(values = c("Total" = "darkgreen", "Peligrosas" = "red")) +
  guides(fill = guide_legend(title = "Por especie")) +
  theme_minimal()+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

training_data %>% filter(especie == "Algarrobo")

3.b

hist_data <- hist(training_data$circ_tronco_cm, breaks=seq(min(training_data$circ_tronco_cm) - 2, max(training_data$circ_tronco_cm) + 10, by=10), main="Histograma de Circ Tronco", xlab="Circ Tronco", ylab="Frecuencia", col="lightblue", border="black")

## plot(hist_data)

3.c

No es una pregunta.

No tiene mucho sentido hacer esto, ya que solo nos da la suma total por clase, que ya contamos antes.

hist_data <- hist(training_data$inclinacion_peligrosa, breaks=seq(min(training_data$inclinacion_peligrosa) - 0.5, max(training_data$inclinacion_peligrosa) + 0.5, by=1), main="Histograma de Inclinacion peligrosa", xlab="Inclinacion peligrosa", ylab="Frecuencia", col="lightblue", border="black")

## plot(hist_data)
result <- training_data %>%
  mutate(
    circ_tronco_cm_cat = case_when(
      circ_tronco_cm <= quantile(circ_tronco_cm, 0.25) ~ "bajo",
      circ_tronco_cm <= quantile(circ_tronco_cm, 0.50) ~ "medio",
      circ_tronco_cm <= quantile(circ_tronco_cm, 0.75) ~ "alto",
      TRUE ~ "muy alto"
    )
  ) %>% arrange(circ_tronco_cm)
ggplot(result, aes(x = seq(1, nrow(result), by=1), y = circ_tronco_cm, color = circ_tronco_cm_cat)) +
  geom_point() +
  labs(title = "Q Circ Tronco CM",
       x = "Sorted Index",
       y = "CM Circ Tronco") +
  scale_color_manual(values = c("red", "blue", "green", "magenta"))  # Customize colors if needed


write.csv(result, file = "~/workspace/arbolado-mza/arbolado-mza-dataset.csv/arbolado-mza-dataset-circ_tronco_cm-train.csv", row.names = FALSE)

4.a/b/c/d / 6

get_confusion <- function(datay, datay_hat) table(Actual = factor(datay, levels=c(0,1)), Predicted = factor(datay_hat, levels=c(0,1)))
get_stats <- function(confusion){
  TP <- confusion[1, 1]
  TN <- confusion[2, 2]
  FP <- confusion[2, 1]
  FN <- confusion[1, 2]
  
  accuracy <- (TP + TN) / sum(confusion)
  precision <- TP / (TP + FP)
  sensitivity <- TP / (TP + FN)
  specificity <- TN / (TN + FP)
  
  return(data.frame(accuracy=accuracy, precision=precision, sensitivity=sensitivity, specificity=specificity))
}
set.seed(123)
random_classifier <- function(training_data, p=0.5) function(testing_data) testing_data %>% mutate(y_hat_5050 = ifelse(runif(nrow(testing_data)) > p, 1, 0))
random_predictions <- random_classifier(training_data)(testing_data)
confusion_matrix <- get_confusion(random_predictions$inclinacion_peligrosa, random_predictions$y_hat_5050)
confusion_matrix
      Predicted
Actual    0    1
     0 2850 2788
     1  380  364
get_stats(confusion_matrix)

5.a/b/c/d / 6

biggerclass_classifier <- function(train_data){
  temp <- testing_data %>% group_by(inclinacion_peligrosa) %>% summarize(count=n()) %>% arrange(inclinacion_peligrosa)
  selected_class <- temp[which.max(tab$count), 1]$inclinacion_peligrosa
  return(function (test_data) (test_data %>% mutate(y_hat_bigger = selected_class)))
}
bigger_predictions <- biggerclass_classifier(training_data)(testing_data)
confusion_matrix <- get_confusion(bigger_predictions$inclinacion_peligrosa, bigger_predictions$y_hat_bigger)
confusion_matrix
      Predicted
Actual    0    1
     0 5638    0
     1  744    0
get_stats(confusion_matrix)

7

tree_classifier <- function(train_data,weights=NULL){
  train_data$inclinacion_peligrosa <- factor(train_data$inclinacion_peligrosa)
  tree_model<-rpart(inclinacion_peligrosa~altura+circ_tronco_cm+lat+long+seccion, data=train_data, weights = weights, minsplit=1, minbucket=1)
  print(tree_model)
  summary(tree_model)
  return(function (test_data){
    temp <- data.frame(test_data)
    temp$y_hat_tree <- predict(tree_model, test_data, type="c")
    return(temp)
  })
}

# Attempt to train the tree with a nearer to 50% split in data, otherwise it just always returns class 0
counts <- training_data %>% group_by(inclinacion_peligrosa) %>% summarize(count=n())
ratio <- (counts[2, 2] / counts[1, 2])[1,1]

Kaggle

encode_danger <- function(data, column, out) data %>%
        dplyr::group_by(!!sym(column)) %>%
        dplyr::summarize(count = sum(inclinacion_peligrosa == 1), total = n()) %>%
        dplyr::mutate({{out}} := count / total) %>%
        dplyr::select(-count, -total)
test_model_output <- function(model, filename){
  root_path <- "~/workspace/arbolado-mza/"
  test.data.set <- read.csv(paste0(root_path, "arbolado-mza-dataset-test.csv/arbolado-mza-dataset-test.csv"))
  
  result.test <- model(testing_data)
  print(result.test)
  confusion_matrix <- get_confusion(result.test$inclinacion_peligrosa, result.test$y_hat_tree)
  print(confusion_matrix)
  print(get_stats(confusion_matrix))
  
  result.validation <- model(test.data.set)
  columns <- data.frame(id = as.numeric(result.validation$id), inclinacion_peligrosa = as.numeric(result.validation$y_hat_tree) - 1)
  write.csv(columns, file = paste0(root_path, "predictions/", filename), row.names=F, append=FALSE, quote=FALSE)
}

Con undersampling

t <- random_classifier(training_data, ratio)(training_data) %>% filter(inclinacion_peligrosa == 1 | y_hat_5050 == 0)
t %>% group_by(inclinacion_peligrosa) %>% summarize(count=n())
model <- tree_classifier(t)
test_model_output(model, "simple_tree_123.csv")

weights <- ifelse(training_data$inclinacion_peligrosa == 1, (1-ratio), ratio)
model <- tree_classifier(training_data, weights)
test_model_output(model, "weighted_tree_123.csv")
rf_classifier <- function(train_data, weights=NULL){
  train_data$inclinacion_peligrosa <- factor(train_data$inclinacion_peligrosa)
  rf_model<-randomForest(inclinacion_peligrosa~altura+circ_tronco_cm+lat+long+seccion, data=train_data, weights = weights, minsplit=1, minbucket=1, ntree=500)
  print(rf_model)
  summary(rf_model)
  return(function (test_data){
    temp <- data.frame(test_data)
    temp$y_hat_tree <- predict(rf_model, test_data, type="c")
    return(temp)
  })
}

weights <- ifelse(training_data$inclinacion_peligrosa == 1, 1*(1-ratio), ratio)
model <- rf_classifier(training_data, weights)
test_model_output(model, "weighted_rf_123.csv")
# F Engineered RF
rf_fe_classifier <- function(train_data, weights=NULL){
  train_data <- data.frame(train_data)
  especie_a <- encode_danger(train_data, "especie", "especie_a")
  altura_a <- encode_danger(train_data, "altura", "altura_a")
  diametro_tronco_a <- encode_danger(train_data, "diametro_tronco", "diametro_tronco_a")
  nombre_seccion_a <- encode_danger(train_data, "nombre_seccion", "nombre_seccion_a")
  
  train_data <- train_data %>%
    left_join(especie_a, by = "especie") %>%
    left_join(nombre_seccion_a, by = "nombre_seccion") %>%
    left_join(diametro_tronco_a, by = "diametro_tronco") %>%
    left_join(altura_a, by = "altura")
  print(train_data)
  train_data$inclinacion_peligrosa <- factor(train_data$inclinacion_peligrosa)
  rf_model<-randomForest(inclinacion_peligrosa~altura+circ_tronco_cm+lat+long+seccion+especie_a+nombre_seccion_a+diametro_tronco_a+altura_a, data=train_data, weights = weights, minsplit=1, minbucket=1, ntree=1000)
  print(rf_model)
  summary(rf_model)
  return(function (test_data){
    temp <- data.frame(test_data) %>%
      left_join(especie_a, by = "especie") %>%
      left_join(nombre_seccion_a, by = "nombre_seccion") %>%
      left_join(diametro_tronco_a, by = "diametro_tronco") %>%
      left_join(altura_a, by = "altura")
    temp$y_hat_tree <- predict(rf_model, temp, type="c")
    return(temp)
  })
}

t <- random_classifier(training_data, ratio)(training_data) %>% filter(inclinacion_peligrosa == 1 | y_hat_5050 == 0)
t %>% group_by(inclinacion_peligrosa) %>% summarize(count=n())

weights <- ifelse(training_data$inclinacion_peligrosa == 1, 1*(1-ratio), ratio)
weights_t <- ifelse(t$inclinacion_peligrosa == 1, 1*(1-ratio), ratio)

model <- rf_fe_classifier(training_data, weights)

Call:
 randomForest(formula = inclinacion_peligrosa ~ altura + circ_tronco_cm +      lat + long + seccion + especie_a + nombre_seccion_a + diametro_tronco_a +      altura_a, data = train_data, weights = weights, minsplit = 1,      minbucket = 1, ntree = 1000) 
               Type of random forest: classification
                     Number of trees: 1000
No. of variables tried at each split: 3

        OOB estimate of  error rate: 22.44%
Confusion matrix:
      0    1 class.error
0 18174 4495   0.1982884
1  1233 1628   0.4309682
test_model_output(model, "weighted_rf_fe_123.csv")
      Predicted
Actual    0    1
     0 4554 1109
     1  288  430
Warning: attempt to set 'append' ignored
model <- rf_fe_classifier(t, weights_t)

Call:
 randomForest(formula = inclinacion_peligrosa ~ altura + circ_tronco_cm +      lat + long + seccion + especie_a + nombre_seccion_a + diametro_tronco_a +      altura_a, data = train_data, weights = weights, minsplit = 1,      minbucket = 1, ntree = 1000) 
               Type of random forest: classification
                     Number of trees: 1000
No. of variables tried at each split: 3

        OOB estimate of  error rate: 37.71%
Confusion matrix:
    0    1 class.error
0 862 2047  0.70367824
1 129 2732  0.04508913
test_model_output(model, "weighted_rf_fe_123_2.csv")
      Predicted
Actual    0    1
     0 1704 3958
     1   37  681
Warning: attempt to set 'append' ignored
rf_cv_classifier <- function(train_data, weights=NULL, folds=10){
  target_column_index <- which(colnames(train_data) == "inclinacion_peligrosa")

  preProc <- preProcess(train_data[, -target_column_index], method = c("center", "scale"))
  training_data_norm <- predict(preProc, train_data[, -target_column_index])
  training_data_norm <- cbind(training_data_norm, inclinacion_peligrosa=train_data$inclinacion_peligrosa)
  print(training_data_norm)
  
  ctrl <- trainControl(method = "cv", number = folds, verboseIter = TRUE)
  set.seed(123)  # for reproducibility
  model <- train(inclinacion_peligrosa~altura+circ_tronco_cm+lat+long+seccion, data = training_data_norm, method = "rf", ntree=5, trControl = ctrl,weights=weights)

  results <- resamples(list(model,model))
  print(model)
  summary(results)
  


  
  # train_data$inclinacion_peligrosa <- factor(train_data$inclinacion_peligrosa)
  # rf_model<-randomForest(inclinacion_peligrosa~altura+circ_tronco_cm+lat+long+seccion, data=train_data, weights = weights, minsplit=1, minbucket=1, ntree=5)
  
  return(function (test_data){
    temp <- data.frame(test_data)
    new_data_norm <- predict(preProc, test_data)
    print(new_data_norm)
    temp$y_hat_tree <- ifelse(predict(model, new_data_norm) > 0.5, 2, 1)
    return(temp)
  })
}



# weights <- ifelse(training_data$inclinacion_peligrosa == 1, 1*(1-ratio), ratio)
# t <- random_classifier(training_data, ratio)(training_data) %>% filter(inclinacion_peligrosa == 1 | y_hat_5050 == 0)
# t %>% group_by(inclinacion_peligrosa) %>% summarize(count=n())

model <- rf_cv_classifier(training_data, weights, folds=10)
Warning: You are trying to do regression and your outcome only has two possible values Are you trying to do classification? If so, use a 2 level factor as your outcome column.
+ Fold01: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold01: mtry=2 
+ Fold01: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold01: mtry=4 
+ Fold01: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold01: mtry=7 
+ Fold02: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold02: mtry=2 
+ Fold02: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold02: mtry=4 
+ Fold02: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold02: mtry=7 
+ Fold03: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold03: mtry=2 
+ Fold03: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold03: mtry=4 
+ Fold03: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold03: mtry=7 
+ Fold04: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold04: mtry=2 
+ Fold04: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold04: mtry=4 
+ Fold04: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold04: mtry=7 
+ Fold05: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold05: mtry=2 
+ Fold05: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold05: mtry=4 
+ Fold05: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold05: mtry=7 
+ Fold06: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold06: mtry=2 
+ Fold06: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold06: mtry=4 
+ Fold06: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold06: mtry=7 
+ Fold07: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold07: mtry=2 
+ Fold07: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold07: mtry=4 
+ Fold07: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold07: mtry=7 
+ Fold08: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold08: mtry=2 
+ Fold08: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold08: mtry=4 
+ Fold08: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold08: mtry=7 
+ Fold09: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold09: mtry=2 
+ Fold09: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold09: mtry=4 
+ Fold09: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold09: mtry=7 
+ Fold10: mtry=2 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold10: mtry=2 
+ Fold10: mtry=4 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold10: mtry=4 
+ Fold10: mtry=7 
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
- Fold10: mtry=7 
Aggregating results
Selecting tuning parameters
Fitting mtry = 2 on full training set
Warning: The response has five or fewer unique values.  Are you sure you want to do regression?
Random Forest 

25530 samples
    5 predictor

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 22977, 22977, 22977, 22977, 22977, 22977, ... 
Resampling results across tuning parameters:

  mtry  RMSE       Rsquared    MAE      
  2     0.3120164  0.03536875  0.1876756
  4     0.3293123  0.03431422  0.1827660
  7     0.3346939  0.03034488  0.1847077

RMSE was used to select the optimal model using the smallest value.
The final value used for the model was mtry = 2.
#model <- rf_cv_classifier(t)
test_model_output(model, "weighted_rf_cv_123.csv")
      Predicted
Actual    0    1
     0    0 5653
     1    0  712
Warning: attempt to set 'append' ignored
ggplot(arbolado.mza.dataset, aes(x = lat, y = long, color = especie)) +
  geom_point(aes(alpha=altura), size = 0.3) +
  labs(
    title = "Scatter Plot of Lat vs. Long",
    x = "Latitude",
    y = "Longitude",
    color = "Inclinacion Peligrosa"
  ) +
  #scale_color_gradient(low = "red", high = "green")
  scale_color_manual(values = c(
  "#FF5733", "#337DFF", "#33FF49", "#A833FF", "#FF9B33", "#FF33B6", "#754A23", "#33F4FF",
  "#E133FF", "#FFFA33", "#000000", "#808080", "#AA0000", "#0000AA", "#00AA00", "#AA00AA",
  "#FF5500", "#FF0088", "#5C3317", "#0088FF", "#FF33A1", "#D9B066", "#3E4E50", "#D35400",
  "#E74C3C", "#3498DB", "#229954", "#7D3C98", "#F4D03F", "#34495E", "#F22613", "#2E86C1"
)
)
# install.packages("plotly")
library(plotly)

# Assuming df is your dataframe with columns lat, long, altura, and especie
plot_ly(data = arbolado.mza.dataset, x = ~lat, y = ~long, z = ~altura, color = ~especie, colors = "Set1") %>%
  add_markers(marker = list(size = 2)) %>%
  layout(
    scene = list(
      xaxis = list(title = "Latitude"),
      yaxis = list(title = "Longitude"),
      zaxis = list(title = "Altura")
    ),
    legend = list(title = "Especie"),
    title = "3D Scatter Plot"
  )
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
Warning: n too large, allowed maximum for palette Set1 is 9
Returning the palette you asked for with that many colors
LS0tCnRpdGxlOiAidHA3LWVkYS5tZCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCmluc3RhbGwucGFja2FnZXMoImNhcmV0IikKaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikKYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkodXRpbHMpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpgYGAKCmBgYHtyfQphcmJvbGFkby5temEuZGF0YXNldCA8LSByZWFkLmNzdigifi93b3Jrc3BhY2UvYXJib2xhZG8tbXphL2FyYm9sYWRvLW16YS1kYXRhc2V0LmNzdi9hcmJvbGFkby1temEtZGF0YXNldC5jc3YiKQphcmJvbGFkby5temEuZGF0YXNldCA8LSBhcy5kYXRhLmZyYW1lKGFyYm9sYWRvLm16YS5kYXRhc2V0KQpgYGAKCiMgMS5hCgpgYGB7cn0KaW5kaWNlcyA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGFyYm9sYWRvLm16YS5kYXRhc2V0JGluY2xpbmFjaW9uX3BlbGlncm9zYSwgcCA9IDAuOCwgbGlzdCA9IEZBTFNFKQoKdHJhaW5pbmdfZGF0YSA8LSBhcmJvbGFkby5temEuZGF0YXNldFtpbmRpY2VzLCBdCnRlc3RpbmdfZGF0YSA8LSBhcmJvbGFkby5temEuZGF0YXNldFstaW5kaWNlcywgXQp3cml0ZS5jc3YodGVzdGluZ19kYXRhLCAiYXJib2xhZG8tbWVuZG96YS12YWxpZGF0aW9uLmNzdiIpCndyaXRlLmNzdih0cmFpbmluZ19kYXRhLCAiYXJib2xhZG8tbWVuZG96YS5jc3YiKQp0ZXN0aW5nX2RhdGEKYGBgCiMgMi5hCgpQYXJlY2VuIGhhYmVyIG11Y2hvcyAoMjI2OTUgdnMgMjgzNSkgbWFzIGVqZW1wbGFyZXMgY29uIGluY2xpbmFjaW9uIG5vIHBlbGlncm9zYQoKYGBge3J9CnJlc3VsdCA8LSB0cmFpbmluZ19kYXRhICU+JQogIGRwbHlyOjpncm91cF9ieShpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoQ291bnQ9bigpKQp4X3ZhbCA8LSBiYXJwbG90KHJlc3VsdCRDb3VudCwgbmFtZXMuYXJnPWMoIk5vIFBlbGlncm9zYSIsICJQZWxpZ3Jvc2EiKSkKIyBBZGQgdGV4dCBsYWJlbHMgb24gdG9wIG9mIHRoZSBiYXJzCnRleHQoeCA9IHhfdmFsLCB5ID0gcmVzdWx0JENvdW50LCBsYWJlbHMgPSByZXN1bHQkQ291bnQsIHBvcyA9IDEsIGNleCA9IDEpCmBgYAojIDIuYgoKTGEgc2VjY2lvbiBtYXMgcGVsaWdyb3NhIHBhcmVjZSBzZXIgbGEgc2VjY2lvbiAzLgoKQ2FiZSBkZXN0YWNhciBxdWUgbm8gdGVuZW1vcyBpbmZvcm1hY2lvbiBwYXJhIGxhcyBzZWNjaW9uZXMgOSB5IDEwIHkgbXV5IHBvY2EgaW5mbyBkZSBsYXMgc2VjY2lvbmVzIDcgeSA4CgpgYGB7cn0KcmVzdWx0IDwtIHRyYWluaW5nX2RhdGEgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHNlY2Npb24pICU+JQogIGRwbHlyOjpzdW1tYXJpemUocGVsaWdyb3NhPXN1bShpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EpLCB0b3RhbD1uKCkpCmdncGxvdChkYXRhPXJlc3VsdCwgYWVzKHg9c2VjY2lvbiwgZmlsbD0iYSIpKSArCiAgZ2VvbV9iYXIoYWVzKHk9dG90YWwpLCBzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIiwgYWxwaGE9MSwgd2lkdGg9MC43LCBmaWxsPSJkYXJrZ3JlZW4iKSArCiAgbGFicyh0aXRsZT0iUG9yIHNlY2Npb24iLCB4PSJTZWNjaW9uIiwgeT0iVG90YWwiKSArCiAgZ2VvbV9iYXIoYWVzKHk9cGVsaWdyb3NhKSwgZGF0YSA9IHJlc3VsdCwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uPSJkb2RnZSIsIGFscGhhID0gMSwgd2lkdGg9MC43LCBmaWxsPSJyZWQiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHJlc3VsdCRzZWNjaW9uLCBleHBhbmQgPSBjKDAsIDApKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJUb3RhbCIgPSAiZGFya2dyZWVuIiwgIlBlbGlncm9zYXMiID0gInJlZCIpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiUG9yIHNlY2Npb24iKSkgKwogIHRoZW1lX21pbmltYWwoKQoKcmVzdWx0IDwtIHRyYWluaW5nX2RhdGEgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHNlY2Npb24pICU+JQogIGRwbHlyOjpzdW1tYXJpemUocGVsaWdyb3NhPXN1bShpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EpL2lmZWxzZShuKCkgPT0gMCwgc3VtKGluY2xpbmFjaW9uX3BlbGlncm9zYSksIG4oKSksIHRvdGFsPTEpCgpnZ3Bsb3QoZGF0YT1yZXN1bHQsIGFlcyh4PXNlY2Npb24sIGZpbGw9ImEiKSkgKwogIGdlb21fYmFyKGFlcyh5PXRvdGFsKSwgc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIsIGFscGhhPTEsIHdpZHRoPTAuNywgZmlsbD0iZGFya2dyZWVuIikgKwogIGxhYnModGl0bGU9IlBvciBzZWNjaW9uIiwgeD0iU2VjY2lvbiIsIHk9IlBlbGlncm9zaWRhZCIpICsKICBnZW9tX2JhcihhZXMoeT1wZWxpZ3Jvc2EpLCBkYXRhID0gcmVzdWx0LCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249ImRvZGdlIiwgYWxwaGEgPSAxLCB3aWR0aD0wLjcsIGZpbGw9InJlZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gcmVzdWx0JHNlY2Npb24sIGV4cGFuZCA9IGMoMCwgMCkpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlRvdGFsIiA9ICJkYXJrZ3JlZW4iLCAiUGVsaWdyb3NhcyIgPSAicmVkIikpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJQb3Igc2VjY2lvbiBOb3JtYWxpemFkYSIpKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAojIDMuYwoKQWwgcGFyZWNlciB0ZW5lbW9zIG11Y2hpc2ltYSBpbmZvcm1hY2lvbiBkZSBNb3JlcmFzLCBlbiBjb21wYXJhY2lvbiBkZSBvdHJhcyBlc3BlY2llcy4KCkF1biBhc2ksIGxvcyBhbGdhcnJvYm9zIHBhcmVjZW4gcHJlc2VudGFyIHVuYSBpbmNsaW5hY2lvbiBwZWxpZ3Jvc2EgY29uIG1heW9yIGZyZXF1ZW5jaWEuCgpEaWNobyBlc3RvLCBzb2xvIHRlbmVtb3MgNSBlamVtcGxhcmVzIGRlIGFsZ2Fycm9ibyBlbiBudWVzdHJvIGRhdGFzZXQgZGUgZW50cmVuYW1pZW50by4KCmBgYHtyfQpyZXN1bHQgPC0gdHJhaW5pbmdfZGF0YSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZXNwZWNpZSkgJT4lCiAgZHBseXI6OnN1bW1hcml6ZShwZWxpZ3Jvc2E9c3VtKGluY2xpbmFjaW9uX3BlbGlncm9zYSksIHRvdGFsPW4oKSkKCnJlc3VsdCRlc3BlY2llID0gcmVvcmRlcihyZXN1bHQkZXNwZWNpZSwgLXJlc3VsdCR0b3RhbCkKZ2dwbG90KGRhdGE9cmVzdWx0LCBhZXMoeD1lc3BlY2llLCBmaWxsPSJhIikpICsKICBnZW9tX2JhcihhZXMoeT10b3RhbCksIHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYT0xLCB3aWR0aD0wLjcsIGZpbGw9ImRhcmtncmVlbiIpICsKICBsYWJzKHRpdGxlPSJQb3IgZXNwZWNpZSIsIHg9IkVzcGVjaWUiLCB5PSJUb3RhbCIpICsKICBnZW9tX2JhcihhZXMoeT1wZWxpZ3Jvc2EpLCBkYXRhID0gcmVzdWx0LCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249ImRvZGdlIiwgYWxwaGEgPSAxLCB3aWR0aD0wLjcsIGZpbGw9InJlZCIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9IHJlc3VsdCRlc3BlY2llLCBleHBhbmQgPSBjKDAsIDApKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJUb3RhbCIgPSAiZGFya2dyZWVuIiwgIlBlbGlncm9zYXMiID0gInJlZCIpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiUG9yIGVzcGVjaWUiKSkgKwogIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKCnJlc3VsdCA8LSB0cmFpbmluZ19kYXRhICU+JQogIGRwbHlyOjpncm91cF9ieShlc3BlY2llKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKHBlbGlncm9zYT1zdW0oaW5jbGluYWNpb25fcGVsaWdyb3NhKS9pZmVsc2UobigpID09IDAsIHN1bShpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EpLCBuKCkpLCB0b3RhbD0xKQoKcmVzdWx0JGVzcGVjaWUgPSByZW9yZGVyKHJlc3VsdCRlc3BlY2llLCAtcmVzdWx0JHBlbGlncm9zYSkKZ2dwbG90KGRhdGE9cmVzdWx0LCBhZXMoeD1lc3BlY2llLCBmaWxsPSJhIikpICsKICBnZW9tX2JhcihhZXMoeT10b3RhbCksIHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYT0xLCB3aWR0aD0wLjcsIGZpbGw9ImRhcmtncmVlbiIpICsKICBsYWJzKHRpdGxlPSJQb3IgZXNwZWNpZSIsIHg9IkVzcGVjaWUiLCB5PSJUb3RhbCIpICsKICBnZW9tX2JhcihhZXMoeT1wZWxpZ3Jvc2EpLCBkYXRhID0gcmVzdWx0LCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249ImRvZGdlIiwgYWxwaGEgPSAxLCB3aWR0aD0wLjcsIGZpbGw9InJlZCIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9IHJlc3VsdCRlc3BlY2llLCBleHBhbmQgPSBjKDAsIDApKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJUb3RhbCIgPSAiZGFya2dyZWVuIiwgIlBlbGlncm9zYXMiID0gInJlZCIpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiUG9yIGVzcGVjaWUiKSkgKwogIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpgYGAKCmBgYHtyfQp0cmFpbmluZ19kYXRhICU+JSBmaWx0ZXIoZXNwZWNpZSA9PSAiQWxnYXJyb2JvIikKYGBgCgojIDMuYgoKYGBge3J9Cmhpc3RfZGF0YSA8LSBoaXN0KHRyYWluaW5nX2RhdGEkY2lyY190cm9uY29fY20sIGJyZWFrcz1zZXEobWluKHRyYWluaW5nX2RhdGEkY2lyY190cm9uY29fY20pIC0gMiwgbWF4KHRyYWluaW5nX2RhdGEkY2lyY190cm9uY29fY20pICsgMTAsIGJ5PTEwKSwgbWFpbj0iSGlzdG9ncmFtYSBkZSBDaXJjIFRyb25jbyIsIHhsYWI9IkNpcmMgVHJvbmNvIiwgeWxhYj0iRnJlY3VlbmNpYSIsIGNvbD0ibGlnaHRibHVlIiwgYm9yZGVyPSJibGFjayIpCiMjIHBsb3QoaGlzdF9kYXRhKQpgYGAKIyAzLmMKCk5vIGVzIHVuYSBwcmVndW50YS4KCk5vIHRpZW5lIG11Y2hvIHNlbnRpZG8gaGFjZXIgZXN0bywgeWEgcXVlIHNvbG8gbm9zIGRhIGxhIHN1bWEgdG90YWwgcG9yIGNsYXNlLCBxdWUgeWEgY29udGFtb3MgYW50ZXMuCgpgYGB7cn0KaGlzdF9kYXRhIDwtIGhpc3QodHJhaW5pbmdfZGF0YSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EsIGJyZWFrcz1zZXEobWluKHRyYWluaW5nX2RhdGEkaW5jbGluYWNpb25fcGVsaWdyb3NhKSAtIDAuNSwgbWF4KHRyYWluaW5nX2RhdGEkaW5jbGluYWNpb25fcGVsaWdyb3NhKSArIDAuNSwgYnk9MSksIG1haW49Ikhpc3RvZ3JhbWEgZGUgSW5jbGluYWNpb24gcGVsaWdyb3NhIiwgeGxhYj0iSW5jbGluYWNpb24gcGVsaWdyb3NhIiwgeWxhYj0iRnJlY3VlbmNpYSIsIGNvbD0ibGlnaHRibHVlIiwgYm9yZGVyPSJibGFjayIpCiMjIHBsb3QoaGlzdF9kYXRhKQoKYGBgCgpgYGB7cn0KcmVzdWx0IDwtIHRyYWluaW5nX2RhdGEgJT4lCiAgbXV0YXRlKAogICAgY2lyY190cm9uY29fY21fY2F0ID0gY2FzZV93aGVuKAogICAgICBjaXJjX3Ryb25jb19jbSA8PSBxdWFudGlsZShjaXJjX3Ryb25jb19jbSwgMC4yNSkgfiAiYmFqbyIsCiAgICAgIGNpcmNfdHJvbmNvX2NtIDw9IHF1YW50aWxlKGNpcmNfdHJvbmNvX2NtLCAwLjUwKSB+ICJtZWRpbyIsCiAgICAgIGNpcmNfdHJvbmNvX2NtIDw9IHF1YW50aWxlKGNpcmNfdHJvbmNvX2NtLCAwLjc1KSB+ICJhbHRvIiwKICAgICAgVFJVRSB+ICJtdXkgYWx0byIKICAgICkKICApICU+JSBhcnJhbmdlKGNpcmNfdHJvbmNvX2NtKQpnZ3Bsb3QocmVzdWx0LCBhZXMoeCA9IHNlcSgxLCBucm93KHJlc3VsdCksIGJ5PTEpLCB5ID0gY2lyY190cm9uY29fY20sIGNvbG9yID0gY2lyY190cm9uY29fY21fY2F0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJRIENpcmMgVHJvbmNvIENNIiwKICAgICAgIHggPSAiU29ydGVkIEluZGV4IiwKICAgICAgIHkgPSAiQ00gQ2lyYyBUcm9uY28iKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJibHVlIiwgImdyZWVuIiwgIm1hZ2VudGEiKSkgICMgQ3VzdG9taXplIGNvbG9ycyBpZiBuZWVkZWQKCndyaXRlLmNzdihyZXN1bHQsIGZpbGUgPSAifi93b3Jrc3BhY2UvYXJib2xhZG8tbXphL2FyYm9sYWRvLW16YS1kYXRhc2V0LmNzdi9hcmJvbGFkby1temEtZGF0YXNldC1jaXJjX3Ryb25jb19jbS10cmFpbi5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKIyA0LmEvYi9jL2QgLyA2CgpgYGB7cn0KZ2V0X2NvbmZ1c2lvbiA8LSBmdW5jdGlvbihkYXRheSwgZGF0YXlfaGF0KSB0YWJsZShBY3R1YWwgPSBmYWN0b3IoZGF0YXksIGxldmVscz1jKDAsMSkpLCBQcmVkaWN0ZWQgPSBmYWN0b3IoZGF0YXlfaGF0LCBsZXZlbHM9YygwLDEpKSkKZ2V0X3N0YXRzIDwtIGZ1bmN0aW9uKGNvbmZ1c2lvbil7CiAgVFAgPC0gY29uZnVzaW9uWzEsIDFdCiAgVE4gPC0gY29uZnVzaW9uWzIsIDJdCiAgRlAgPC0gY29uZnVzaW9uWzIsIDFdCiAgRk4gPC0gY29uZnVzaW9uWzEsIDJdCiAgCiAgYWNjdXJhY3kgPC0gKFRQICsgVE4pIC8gc3VtKGNvbmZ1c2lvbikKICBwcmVjaXNpb24gPC0gVFAgLyAoVFAgKyBGUCkKICBzZW5zaXRpdml0eSA8LSBUUCAvIChUUCArIEZOKQogIHNwZWNpZmljaXR5IDwtIFROIC8gKFROICsgRlApCiAgCiAgcmV0dXJuKGRhdGEuZnJhbWUoYWNjdXJhY3k9YWNjdXJhY3ksIHByZWNpc2lvbj1wcmVjaXNpb24sIHNlbnNpdGl2aXR5PXNlbnNpdGl2aXR5LCBzcGVjaWZpY2l0eT1zcGVjaWZpY2l0eSkpCn0KYGBgCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpyYW5kb21fY2xhc3NpZmllciA8LSBmdW5jdGlvbih0cmFpbmluZ19kYXRhLCBwPTAuNSkgZnVuY3Rpb24odGVzdGluZ19kYXRhKSB0ZXN0aW5nX2RhdGEgJT4lIG11dGF0ZSh5X2hhdF81MDUwID0gaWZlbHNlKHJ1bmlmKG5yb3codGVzdGluZ19kYXRhKSkgPiBwLCAxLCAwKSkKcmFuZG9tX3ByZWRpY3Rpb25zIDwtIHJhbmRvbV9jbGFzc2lmaWVyKHRyYWluaW5nX2RhdGEpKHRlc3RpbmdfZGF0YSkKY29uZnVzaW9uX21hdHJpeCA8LSBnZXRfY29uZnVzaW9uKHJhbmRvbV9wcmVkaWN0aW9ucyRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EsIHJhbmRvbV9wcmVkaWN0aW9ucyR5X2hhdF81MDUwKQpjb25mdXNpb25fbWF0cml4CmdldF9zdGF0cyhjb25mdXNpb25fbWF0cml4KQpgYGAKCiMgNS5hL2IvYy9kIC8gNgoKCgoKYGBge3J9CmJpZ2dlcmNsYXNzX2NsYXNzaWZpZXIgPC0gZnVuY3Rpb24odHJhaW5fZGF0YSl7CiAgdGVtcCA8LSB0ZXN0aW5nX2RhdGEgJT4lIGdyb3VwX2J5KGluY2xpbmFjaW9uX3BlbGlncm9zYSkgJT4lIHN1bW1hcml6ZShjb3VudD1uKCkpICU+JSBhcnJhbmdlKGluY2xpbmFjaW9uX3BlbGlncm9zYSkKICBzZWxlY3RlZF9jbGFzcyA8LSB0ZW1wW3doaWNoLm1heCh0YWIkY291bnQpLCAxXSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EKICByZXR1cm4oZnVuY3Rpb24gKHRlc3RfZGF0YSkgKHRlc3RfZGF0YSAlPiUgbXV0YXRlKHlfaGF0X2JpZ2dlciA9IHNlbGVjdGVkX2NsYXNzKSkpCn0KYmlnZ2VyX3ByZWRpY3Rpb25zIDwtIGJpZ2dlcmNsYXNzX2NsYXNzaWZpZXIodHJhaW5pbmdfZGF0YSkodGVzdGluZ19kYXRhKQpjb25mdXNpb25fbWF0cml4IDwtIGdldF9jb25mdXNpb24oYmlnZ2VyX3ByZWRpY3Rpb25zJGluY2xpbmFjaW9uX3BlbGlncm9zYSwgYmlnZ2VyX3ByZWRpY3Rpb25zJHlfaGF0X2JpZ2dlcikKY29uZnVzaW9uX21hdHJpeApnZXRfc3RhdHMoY29uZnVzaW9uX21hdHJpeCkKYGBgCgojIDcKYGBge3J9CnRyZWVfY2xhc3NpZmllciA8LSBmdW5jdGlvbih0cmFpbl9kYXRhLHdlaWdodHM9TlVMTCl7CiAgdHJhaW5fZGF0YSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EgPC0gZmFjdG9yKHRyYWluX2RhdGEkaW5jbGluYWNpb25fcGVsaWdyb3NhKQogIHRyZWVfbW9kZWw8LXJwYXJ0KGluY2xpbmFjaW9uX3BlbGlncm9zYX5hbHR1cmErY2lyY190cm9uY29fY20rbGF0K2xvbmcrc2VjY2lvbiwgZGF0YT10cmFpbl9kYXRhLCB3ZWlnaHRzID0gd2VpZ2h0cywgbWluc3BsaXQ9MSwgbWluYnVja2V0PTEpCiAgcHJpbnQodHJlZV9tb2RlbCkKICBzdW1tYXJ5KHRyZWVfbW9kZWwpCiAgcmV0dXJuKGZ1bmN0aW9uICh0ZXN0X2RhdGEpewogICAgdGVtcCA8LSBkYXRhLmZyYW1lKHRlc3RfZGF0YSkKICAgIHRlbXAkeV9oYXRfdHJlZSA8LSBwcmVkaWN0KHRyZWVfbW9kZWwsIHRlc3RfZGF0YSwgdHlwZT0iYyIpCiAgICByZXR1cm4odGVtcCkKICB9KQp9CgojIEF0dGVtcHQgdG8gdHJhaW4gdGhlIHRyZWUgd2l0aCBhIG5lYXJlciB0byA1MCUgc3BsaXQgaW4gZGF0YSwgb3RoZXJ3aXNlIGl0IGp1c3QgYWx3YXlzIHJldHVybnMgY2xhc3MgMApjb3VudHMgPC0gdHJhaW5pbmdfZGF0YSAlPiUgZ3JvdXBfYnkoaW5jbGluYWNpb25fcGVsaWdyb3NhKSAlPiUgc3VtbWFyaXplKGNvdW50PW4oKSkKcmF0aW8gPC0gKGNvdW50c1syLCAyXSAvIGNvdW50c1sxLCAyXSlbMSwxXQpgYGAKIyBLYWdnbGUKYGBge3J9CmVuY29kZV9kYW5nZXIgPC0gZnVuY3Rpb24oZGF0YSwgY29sdW1uLCBvdXQpIGRhdGEgJT4lCiAgICAgICAgZHBseXI6Omdyb3VwX2J5KCEhc3ltKGNvbHVtbikpICU+JQogICAgICAgIGRwbHlyOjpzdW1tYXJpemUoY291bnQgPSBzdW0oaW5jbGluYWNpb25fcGVsaWdyb3NhID09IDEpLCB0b3RhbCA9IG4oKSkgJT4lCiAgICAgICAgZHBseXI6Om11dGF0ZSh7e291dH19IDo9IGNvdW50IC8gdG90YWwpICU+JQogICAgICAgIGRwbHlyOjpzZWxlY3QoLWNvdW50LCAtdG90YWwpCmBgYAoKYGBge3J9CnRlc3RfbW9kZWxfb3V0cHV0IDwtIGZ1bmN0aW9uKG1vZGVsLCBmaWxlbmFtZSl7CiAgcm9vdF9wYXRoIDwtICJ+L3dvcmtzcGFjZS9hcmJvbGFkby1temEvIgogIHRlc3QuZGF0YS5zZXQgPC0gcmVhZC5jc3YocGFzdGUwKHJvb3RfcGF0aCwgImFyYm9sYWRvLW16YS1kYXRhc2V0LXRlc3QuY3N2L2FyYm9sYWRvLW16YS1kYXRhc2V0LXRlc3QuY3N2IikpCiAgCiAgcmVzdWx0LnRlc3QgPC0gbW9kZWwodGVzdGluZ19kYXRhKQogIHByaW50KHJlc3VsdC50ZXN0KQogIGNvbmZ1c2lvbl9tYXRyaXggPC0gZ2V0X2NvbmZ1c2lvbihyZXN1bHQudGVzdCRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EsIHJlc3VsdC50ZXN0JHlfaGF0X3RyZWUpCiAgcHJpbnQoY29uZnVzaW9uX21hdHJpeCkKICBwcmludChnZXRfc3RhdHMoY29uZnVzaW9uX21hdHJpeCkpCiAgCiAgcmVzdWx0LnZhbGlkYXRpb24gPC0gbW9kZWwodGVzdC5kYXRhLnNldCkKICBjb2x1bW5zIDwtIGRhdGEuZnJhbWUoaWQgPSBhcy5udW1lcmljKHJlc3VsdC52YWxpZGF0aW9uJGlkKSwgaW5jbGluYWNpb25fcGVsaWdyb3NhID0gYXMubnVtZXJpYyhyZXN1bHQudmFsaWRhdGlvbiR5X2hhdF90cmVlKSAtIDEpCiAgd3JpdGUuY3N2KGNvbHVtbnMsIGZpbGUgPSBwYXN0ZTAocm9vdF9wYXRoLCAicHJlZGljdGlvbnMvIiwgZmlsZW5hbWUpLCByb3cubmFtZXM9RiwgYXBwZW5kPUZBTFNFLCBxdW90ZT1GQUxTRSkKfQpgYGAKQ29uIHVuZGVyc2FtcGxpbmcKYGBge3J9CnQgPC0gcmFuZG9tX2NsYXNzaWZpZXIodHJhaW5pbmdfZGF0YSwgcmF0aW8pKHRyYWluaW5nX2RhdGEpICU+JSBmaWx0ZXIoaW5jbGluYWNpb25fcGVsaWdyb3NhID09IDEgfCB5X2hhdF81MDUwID09IDApCnQgJT4lIGdyb3VwX2J5KGluY2xpbmFjaW9uX3BlbGlncm9zYSkgJT4lIHN1bW1hcml6ZShjb3VudD1uKCkpCm1vZGVsIDwtIHRyZWVfY2xhc3NpZmllcih0KQp0ZXN0X21vZGVsX291dHB1dChtb2RlbCwgInNpbXBsZV90cmVlXzEyMy5jc3YiKQpgYGAKCmBgYHtyfQoKd2VpZ2h0cyA8LSBpZmVsc2UodHJhaW5pbmdfZGF0YSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EgPT0gMSwgKDEtcmF0aW8pLCByYXRpbykKbW9kZWwgPC0gdHJlZV9jbGFzc2lmaWVyKHRyYWluaW5nX2RhdGEsIHdlaWdodHMpCnRlc3RfbW9kZWxfb3V0cHV0KG1vZGVsLCAid2VpZ2h0ZWRfdHJlZV8xMjMuY3N2IikKCmBgYAoKYGBge3J9CnJmX2NsYXNzaWZpZXIgPC0gZnVuY3Rpb24odHJhaW5fZGF0YSwgd2VpZ2h0cz1OVUxMKXsKICB0cmFpbl9kYXRhJGluY2xpbmFjaW9uX3BlbGlncm9zYSA8LSBmYWN0b3IodHJhaW5fZGF0YSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EpCiAgcmZfbW9kZWw8LXJhbmRvbUZvcmVzdChpbmNsaW5hY2lvbl9wZWxpZ3Jvc2F+YWx0dXJhK2NpcmNfdHJvbmNvX2NtK2xhdCtsb25nK3NlY2Npb24sIGRhdGE9dHJhaW5fZGF0YSwgd2VpZ2h0cyA9IHdlaWdodHMsIG1pbnNwbGl0PTEsIG1pbmJ1Y2tldD0xLCBudHJlZT01MDApCiAgcHJpbnQocmZfbW9kZWwpCiAgc3VtbWFyeShyZl9tb2RlbCkKICByZXR1cm4oZnVuY3Rpb24gKHRlc3RfZGF0YSl7CiAgICB0ZW1wIDwtIGRhdGEuZnJhbWUodGVzdF9kYXRhKQogICAgdGVtcCR5X2hhdF90cmVlIDwtIHByZWRpY3QocmZfbW9kZWwsIHRlc3RfZGF0YSwgdHlwZT0iYyIpCiAgICByZXR1cm4odGVtcCkKICB9KQp9Cgp3ZWlnaHRzIDwtIGlmZWxzZSh0cmFpbmluZ19kYXRhJGluY2xpbmFjaW9uX3BlbGlncm9zYSA9PSAxLCAxKigxLXJhdGlvKSwgcmF0aW8pCm1vZGVsIDwtIHJmX2NsYXNzaWZpZXIodHJhaW5pbmdfZGF0YSwgd2VpZ2h0cykKdGVzdF9tb2RlbF9vdXRwdXQobW9kZWwsICJ3ZWlnaHRlZF9yZl8xMjMuY3N2IikKYGBgCgpgYGB7cn0KIyBGIEVuZ2luZWVyZWQgUkYKcmZfZmVfY2xhc3NpZmllciA8LSBmdW5jdGlvbih0cmFpbl9kYXRhLCB3ZWlnaHRzPU5VTEwpewogIHRyYWluX2RhdGEgPC0gZGF0YS5mcmFtZSh0cmFpbl9kYXRhKQogIGVzcGVjaWVfYSA8LSBlbmNvZGVfZGFuZ2VyKHRyYWluX2RhdGEsICJlc3BlY2llIiwgImVzcGVjaWVfYSIpCiAgYWx0dXJhX2EgPC0gZW5jb2RlX2Rhbmdlcih0cmFpbl9kYXRhLCAiYWx0dXJhIiwgImFsdHVyYV9hIikKICBkaWFtZXRyb190cm9uY29fYSA8LSBlbmNvZGVfZGFuZ2VyKHRyYWluX2RhdGEsICJkaWFtZXRyb190cm9uY28iLCAiZGlhbWV0cm9fdHJvbmNvX2EiKQogIG5vbWJyZV9zZWNjaW9uX2EgPC0gZW5jb2RlX2Rhbmdlcih0cmFpbl9kYXRhLCAibm9tYnJlX3NlY2Npb24iLCAibm9tYnJlX3NlY2Npb25fYSIpCiAgCiAgdHJhaW5fZGF0YSA8LSB0cmFpbl9kYXRhICU+JQogICAgbGVmdF9qb2luKGVzcGVjaWVfYSwgYnkgPSAiZXNwZWNpZSIpICU+JQogICAgbGVmdF9qb2luKG5vbWJyZV9zZWNjaW9uX2EsIGJ5ID0gIm5vbWJyZV9zZWNjaW9uIikgJT4lCiAgICBsZWZ0X2pvaW4oZGlhbWV0cm9fdHJvbmNvX2EsIGJ5ID0gImRpYW1ldHJvX3Ryb25jbyIpICU+JQogICAgbGVmdF9qb2luKGFsdHVyYV9hLCBieSA9ICJhbHR1cmEiKQogIHByaW50KHRyYWluX2RhdGEpCiAgdHJhaW5fZGF0YSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EgPC0gZmFjdG9yKHRyYWluX2RhdGEkaW5jbGluYWNpb25fcGVsaWdyb3NhKQogIHJmX21vZGVsPC1yYW5kb21Gb3Jlc3QoaW5jbGluYWNpb25fcGVsaWdyb3NhfmFsdHVyYStjaXJjX3Ryb25jb19jbStsYXQrbG9uZytzZWNjaW9uK2VzcGVjaWVfYStub21icmVfc2VjY2lvbl9hK2RpYW1ldHJvX3Ryb25jb19hK2FsdHVyYV9hLCBkYXRhPXRyYWluX2RhdGEsIHdlaWdodHMgPSB3ZWlnaHRzLCBtaW5zcGxpdD0xLCBtaW5idWNrZXQ9MSwgbnRyZWU9MTAwMCkKICBwcmludChyZl9tb2RlbCkKICBzdW1tYXJ5KHJmX21vZGVsKQogIHJldHVybihmdW5jdGlvbiAodGVzdF9kYXRhKXsKICAgIHRlbXAgPC0gZGF0YS5mcmFtZSh0ZXN0X2RhdGEpICU+JQogICAgICBsZWZ0X2pvaW4oZXNwZWNpZV9hLCBieSA9ICJlc3BlY2llIikgJT4lCiAgICAgIGxlZnRfam9pbihub21icmVfc2VjY2lvbl9hLCBieSA9ICJub21icmVfc2VjY2lvbiIpICU+JQogICAgICBsZWZ0X2pvaW4oZGlhbWV0cm9fdHJvbmNvX2EsIGJ5ID0gImRpYW1ldHJvX3Ryb25jbyIpICU+JQogICAgICBsZWZ0X2pvaW4oYWx0dXJhX2EsIGJ5ID0gImFsdHVyYSIpCiAgICB0ZW1wJHlfaGF0X3RyZWUgPC0gcHJlZGljdChyZl9tb2RlbCwgdGVtcCwgdHlwZT0iYyIpCiAgICByZXR1cm4odGVtcCkKICB9KQp9Cgp0IDwtIHJhbmRvbV9jbGFzc2lmaWVyKHRyYWluaW5nX2RhdGEsIHJhdGlvKSh0cmFpbmluZ19kYXRhKSAlPiUgZmlsdGVyKGluY2xpbmFjaW9uX3BlbGlncm9zYSA9PSAxIHwgeV9oYXRfNTA1MCA9PSAwKQp0ICU+JSBncm91cF9ieShpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EpICU+JSBzdW1tYXJpemUoY291bnQ9bigpKQoKd2VpZ2h0cyA8LSBpZmVsc2UodHJhaW5pbmdfZGF0YSRpbmNsaW5hY2lvbl9wZWxpZ3Jvc2EgPT0gMSwgMSooMS1yYXRpbyksIHJhdGlvKQp3ZWlnaHRzX3QgPC0gaWZlbHNlKHQkaW5jbGluYWNpb25fcGVsaWdyb3NhID09IDEsIDEqKDEtcmF0aW8pLCByYXRpbykKCm1vZGVsIDwtIHJmX2ZlX2NsYXNzaWZpZXIodHJhaW5pbmdfZGF0YSwgd2VpZ2h0cykKdGVzdF9tb2RlbF9vdXRwdXQobW9kZWwsICJ3ZWlnaHRlZF9yZl9mZV8xMjMuY3N2IikKbW9kZWwgPC0gcmZfZmVfY2xhc3NpZmllcih0LCB3ZWlnaHRzX3QpCnRlc3RfbW9kZWxfb3V0cHV0KG1vZGVsLCAid2VpZ2h0ZWRfcmZfZmVfMTIzXzIuY3N2IikKCmBgYAoKYGBge3J9CnJmX2N2X2NsYXNzaWZpZXIgPC0gZnVuY3Rpb24odHJhaW5fZGF0YSwgd2VpZ2h0cz1OVUxMLCBmb2xkcz0xMCl7CiAgdGFyZ2V0X2NvbHVtbl9pbmRleCA8LSB3aGljaChjb2xuYW1lcyh0cmFpbl9kYXRhKSA9PSAiaW5jbGluYWNpb25fcGVsaWdyb3NhIikKCiAgcHJlUHJvYyA8LSBwcmVQcm9jZXNzKHRyYWluX2RhdGFbLCAtdGFyZ2V0X2NvbHVtbl9pbmRleF0sIG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQogIHRyYWluaW5nX2RhdGFfbm9ybSA8LSBwcmVkaWN0KHByZVByb2MsIHRyYWluX2RhdGFbLCAtdGFyZ2V0X2NvbHVtbl9pbmRleF0pCiAgdHJhaW5pbmdfZGF0YV9ub3JtIDwtIGNiaW5kKHRyYWluaW5nX2RhdGFfbm9ybSwgaW5jbGluYWNpb25fcGVsaWdyb3NhPXRyYWluX2RhdGEkaW5jbGluYWNpb25fcGVsaWdyb3NhKQogIHByaW50KHRyYWluaW5nX2RhdGFfbm9ybSkKICAKICBjdHJsIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSBmb2xkcywgdmVyYm9zZUl0ZXIgPSBUUlVFKQogIHNldC5zZWVkKDEyMykgICMgZm9yIHJlcHJvZHVjaWJpbGl0eQogIG1vZGVsIDwtIHRyYWluKGluY2xpbmFjaW9uX3BlbGlncm9zYX5hbHR1cmErY2lyY190cm9uY29fY20rbGF0K2xvbmcrc2VjY2lvbiwgZGF0YSA9IHRyYWluaW5nX2RhdGFfbm9ybSwgbWV0aG9kID0gInJmIiwgbnRyZWU9NTAsIHRyQ29udHJvbCA9IGN0cmwsd2VpZ2h0cz13ZWlnaHRzKQoKICByZXN1bHRzIDwtIHJlc2FtcGxlcyhsaXN0KG1vZGVsLG1vZGVsKSkKICBwcmludChtb2RlbCkKICBzdW1tYXJ5KHJlc3VsdHMpCiAgCgoKICAKICAjIHRyYWluX2RhdGEkaW5jbGluYWNpb25fcGVsaWdyb3NhIDwtIGZhY3Rvcih0cmFpbl9kYXRhJGluY2xpbmFjaW9uX3BlbGlncm9zYSkKICAjIHJmX21vZGVsPC1yYW5kb21Gb3Jlc3QoaW5jbGluYWNpb25fcGVsaWdyb3NhfmFsdHVyYStjaXJjX3Ryb25jb19jbStsYXQrbG9uZytzZWNjaW9uLCBkYXRhPXRyYWluX2RhdGEsIHdlaWdodHMgPSB3ZWlnaHRzLCBtaW5zcGxpdD0xLCBtaW5idWNrZXQ9MSwgbnRyZWU9NSkKICAKICByZXR1cm4oZnVuY3Rpb24gKHRlc3RfZGF0YSl7CiAgICB0ZW1wIDwtIGRhdGEuZnJhbWUodGVzdF9kYXRhKQogICAgbmV3X2RhdGFfbm9ybSA8LSBwcmVkaWN0KHByZVByb2MsIHRlc3RfZGF0YSkKICAgIHByaW50KG5ld19kYXRhX25vcm0pCiAgICB0ZW1wJHlfaGF0X3RyZWUgPC0gaWZlbHNlKHByZWRpY3QobW9kZWwsIG5ld19kYXRhX25vcm0pID4gMC41LCAyLCAxKQogICAgcmV0dXJuKHRlbXApCiAgfSkKfQoKCgp3ZWlnaHRzIDwtIGlmZWxzZSh0cmFpbmluZ19kYXRhJGluY2xpbmFjaW9uX3BlbGlncm9zYSA9PSAxLCAxKigxLXJhdGlvKSwgcmF0aW8pCgptb2RlbCA8LSByZl9jdl9jbGFzc2lmaWVyKHRyYWluaW5nX2RhdGEsIHdlaWdodHMsIGZvbGRzPTEwKQojbW9kZWwgPC0gcmZfY3ZfY2xhc3NpZmllcih0KQp0ZXN0X21vZGVsX291dHB1dChtb2RlbCwgIndlaWdodGVkX3JmX2N2XzEyMy5jc3YiKQpgYGAKCgpgYGB7cn0KZ2dwbG90KGFyYm9sYWRvLm16YS5kYXRhc2V0LCBhZXMoeCA9IGxhdCwgeSA9IGxvbmcsIGNvbG9yID0gZXNwZWNpZSkpICsKICBnZW9tX3BvaW50KGFlcyhhbHBoYT1hbHR1cmEpLCBzaXplID0gMC4zKSArCiAgbGFicygKICAgIHRpdGxlID0gIlNjYXR0ZXIgUGxvdCBvZiBMYXQgdnMuIExvbmciLAogICAgeCA9ICJMYXRpdHVkZSIsCiAgICB5ID0gIkxvbmdpdHVkZSIsCiAgICBjb2xvciA9ICJJbmNsaW5hY2lvbiBQZWxpZ3Jvc2EiCiAgKSArCiAgI3NjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJyZWQiLCBoaWdoID0gImdyZWVuIikKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygKICAiI0ZGNTczMyIsICIjMzM3REZGIiwgIiMzM0ZGNDkiLCAiI0E4MzNGRiIsICIjRkY5QjMzIiwgIiNGRjMzQjYiLCAiIzc1NEEyMyIsICIjMzNGNEZGIiwKICAiI0UxMzNGRiIsICIjRkZGQTMzIiwgIiMwMDAwMDAiLCAiIzgwODA4MCIsICIjQUEwMDAwIiwgIiMwMDAwQUEiLCAiIzAwQUEwMCIsICIjQUEwMEFBIiwKICAiI0ZGNTUwMCIsICIjRkYwMDg4IiwgIiM1QzMzMTciLCAiIzAwODhGRiIsICIjRkYzM0ExIiwgIiNEOUIwNjYiLCAiIzNFNEU1MCIsICIjRDM1NDAwIiwKICAiI0U3NEMzQyIsICIjMzQ5OERCIiwgIiMyMjk5NTQiLCAiIzdEM0M5OCIsICIjRjREMDNGIiwgIiMzNDQ5NUUiLCAiI0YyMjYxMyIsICIjMkU4NkMxIgopCikKYGBgCgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKbGlicmFyeShwbG90bHkpCgojIEFzc3VtaW5nIGRmIGlzIHlvdXIgZGF0YWZyYW1lIHdpdGggY29sdW1ucyBsYXQsIGxvbmcsIGFsdHVyYSwgYW5kIGVzcGVjaWUKcGxvdF9seShkYXRhID0gYXJib2xhZG8ubXphLmRhdGFzZXQsIHggPSB+bGF0LCB5ID0gfmxvbmcsIHogPSB+YWx0dXJhLCBjb2xvciA9IH5lc3BlY2llLCBjb2xvcnMgPSAiU2V0MSIpICU+JQogIGFkZF9tYXJrZXJzKG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDIpKSAlPiUKICBsYXlvdXQoCiAgICBzY2VuZSA9IGxpc3QoCiAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJMYXRpdHVkZSIpLAogICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiTG9uZ2l0dWRlIiksCiAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJBbHR1cmEiKQogICAgKSwKICAgIGxlZ2VuZCA9IGxpc3QodGl0bGUgPSAiRXNwZWNpZSIpLAogICAgdGl0bGUgPSAiM0QgU2NhdHRlciBQbG90IgogICkKYGBg